﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using GameEngine.MoveGenerators;
using GameEngine.DrawEvaluator;
using GameEngine.PositionConverter;

namespace GameEngine
{
    /// <summary>
    /// Basic class responsible for evaluating moves and checking rules
    /// </summary>
    public class GameRuleController
    {
        /// <summary>
        /// Reference to gameboard, sets the GameManagerFactory
        /// </summary>
        public GameBoard GameBoard
        { get; set; }

        /// <summary>
        /// Reference to ManMoveGenerator
        /// </summary>
        public IManMoveGenerator ManMoveGenerator
        { get; set; }

        /// <summary>
        /// Reference to king move generator
        /// </summary>
        public IKingMoveGenerator KingMoveGenerator
        { get; set; }

        /// <summary>
        /// Reference to the DrawEvaluator
        /// </summary>
        public IDrawEvaluator DrawEvaluator
        { get; set; }

        /// <summary>
        /// Reference to position converter
        /// </summary>
        public IPositionConverter PositionConverter
        { get; set; }


        /// <summary>
        /// Basic settings of the game
        /// </summary>
        public CheckersSettings CheckersSettings
        { get; set; }

        /// <summary>
        /// Reference to class resolving capture constraints
        /// </summary>
        public CaptureConstraintEvaluator CaptureConstraintEvaluator
        { get; set; }


        public GameRuleController()
        {
        }

        #region Public interface

        /// <summary>
        /// Checks whether a given figure can move
        /// </summary>
        /// <param name="pos">figure we check the move</param>
        /// <param name="figuresToJump">returns number of figures that should jump</param>
        /// <returns>true, if figure can move</returns>
        public bool CanThisFigureMove(Position pos, out List<Position> figuresToJump, bool oMultipleJump = false)
        {
            figuresToJump = new List<Position>();

            enumFigureType figure = GameBoard.GetField(pos);
            if (figure == enumFigureType.Nothing)
                return false;

            if (figure.FigureColor() == GameBoard.OnMove)
            {
                List<Move> lstJumpMoves = CaptureConstraintEvaluator.GetListOfValidJumps(oMultipleJump == true ? (Position?)pos : null);

                if (lstJumpMoves.Count > 0)
                {
                    foreach (Move m in lstJumpMoves)
                        figuresToJump.Add(m.startPos);

                    return figuresToJump.Contains(pos);
                }                
                return true;
            }           
            return false;
        }

        /// <summary>
        /// Checks whether the move is valid
        /// </summary>
        public bool CanFigureMoveHere(Position start, Position end, bool oMultipleJump = false)
        {
            // on white field cannot move
            if (GameBoard.GetFieldColor(end.Row, end.Col) == enumFigureColor.White)
                return false;

            List<Position> lstPossibleMoves = new List<Position>();
            GetFigureMoves(start, lstPossibleMoves, oMultipleJump);

            return lstPossibleMoves.Contains(end);
        }


        /// <summary>
        /// Checks whether a figure must continue with jump. Used for drag&drop operations
        /// </summary>
        /// <param name="previousMove">Previous move done</param>
        /// <param name="pos">Current figure position</param>
        /// <returns></returns>
        public bool MustFigureContinueWithJump(Move previousMove, Position pos)
        {
            if (previousMove.RemovingFigure == false || previousMove.convertedToKing == true)
                return false;

            enumFigureType figureType = GameBoard.GetField(pos);
            if (figureType == enumFigureType.Nothing)
                return false;

            if (figureType.IsKing() == false)
            {
                return ManMoveGenerator.GetManJumps(pos, 
                        CaptureConstraintEvaluator.GetManOrientation(pos)).Count > 0;
            }
            else
            {
                // check ignored direction
                enumDiagonalDir? ignoredDir = previousMove != null ?
                    (previousMove.endPos.GetDiagonalDir(previousMove.startPos)) : (enumDiagonalDir?)null;

                return KingMoveGenerator.GetKingJumps(pos, ignoredDir).Count > 0;
            }
        }

        /// <summary>
        /// Returns valid moves for given figure
        /// </summary>
        public void GetFigureMoves(Position figurePos, List<Position> lstEndPositions, bool oMultipleJump = false)
        {
            List<Move> lstJumps = CaptureConstraintEvaluator.GetListOfValidJumps(oMultipleJump ? (Position?)figurePos : null);
            
            // if there are jumps, check jumps
            if (lstJumps.Count > 0)
            {
                lstEndPositions.AddRange(lstJumps.Where(i => i.startPos == figurePos).Select(i => i.endPos).Distinct().ToList());
            }
            else if(oMultipleJump == false)
            {
                // otherwise check steps
                lstEndPositions.AddRange(GetFigureSteps(figurePos));
            }
        }

        /// <summary>
        /// Checks whether given player can move
        /// </summary>
        public bool CanPlayerMove(enumFigureColor figureColor)
        {
            for(int row = 0; row < GameBoard.Size; row++)
                for (int col = 0; col < GameBoard.Size; col++)
                {
                    enumFigureType figureType = GameBoard.GetField(row, col);
                    if (figureType == enumFigureType.Nothing)
                        continue;

                    if (figureType.FigureColor() == figureColor)
                    {
                        List<Position> list = new List<Position>();
                        GetFigureMoves(new Position(row, col), list);
                        if(list.Count > 0)
                            return true;
                    } // if
                } // for

            return false;
        }

        /// <summary>
        /// Completes the valid move entered by coordinates
        /// </summary>
        public Move CompleteMoveStep(Position start, Position end)
        {
            return ManMoveGenerator.CompleteMoveStep(start, end, CaptureConstraintEvaluator.GetManOrientation(start));
        }

        /// <summary>
        /// Evaluates game result
        /// </summary>
        /// <returns></returns>
        public enumGameResult EvaluateGameResult(List<Move> lstGameHistory)
        {
            // check win/lose
            if (GameBoard.OnMove == enumFigureColor.White)
            {
                if (CanPlayerMove(enumFigureColor.White) == false)
                    return enumGameResult.BlackWin;
            }
            else
            {
                if (CanPlayerMove(enumFigureColor.Black) == false)
                    return enumGameResult.WhiteWin;
            }

            // check draw
            if (DrawEvaluator.IsDraw(lstGameHistory) == true)
                return enumGameResult.Draw;

            // unknown status
            return enumGameResult.Unknown;
        }


        #endregion

        /// <summary>
        /// Protected method returning figure steps
        /// </summary>
        protected List<Position> GetFigureSteps(Position figurePos)
        {
            List<Position> lstSteps = new List<Position>();

            enumFigureType figureType = GameBoard.GetField(figurePos);
            if (figureType == enumFigureType.Nothing)
                return lstSteps;

            if (figureType.IsKing() == false)
            {
                lstSteps = ManMoveGenerator.GetManMoves(figurePos, CaptureConstraintEvaluator.GetManOrientation(figurePos));
            }
            else
            {
                lstSteps = KingMoveGenerator.GetKingMoves(figurePos);
            }

            return lstSteps;
        }

        public bool TryParsePosition(string sPosition, out Position pos)
        {
            return PositionConverter.TryParse(sPosition, out pos);
        }


        public virtual string ToNumberNotation(Position pos)
        {
            return PositionConverter.PositionToString(pos, false);
        }
    }
}
